﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;
using System.Net;
using System.Configuration;
using ExtraClub.Domain.Entities;
using SubSonic;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace ExtraClub.Domain {
    public static class Job {
        public class ThreadContext {
            public EventWaitHandle Autoevent = new AutoResetEvent(false);
            public SmtpClient client = null;
            public VwMailJob item = null;
        }
        private const double JOBMAIL_HOLDON = 15; // minutes
        private static int SmtpServerConnectionLimit = default(int);
        private static List<AutoResetEvent> waitEvents = new List<AutoResetEvent>();

        internal static void SendBatchMail() {
            SmtpServerConnectionLimit = int.Parse(ConfigurationManager.AppSettings.Get("SmtpServerConnectionLimit"));

            VwMailJobCollection jobs = Select.AllColumnsFrom<VwMailJob>()
               .WhereExpression(VwMailJob.Columns.Sent).IsNull()
                   .And(VwMailJob.Columns.Suspended).IsEqualTo(false)
                   .And(VwMailJob.Columns.Created).IsLessThan(DateTime.Now.AddMinutes(-JOBMAIL_HOLDON))
                   .AndExpression(VwMailJob.Columns.AttemptNumber).IsLessThan(SmtpServerConnectionLimit)
                   .Or(VwMailJob.Columns.AttemptNumber).IsNull()
                   .ExecuteAsCollection<VwMailJobCollection>();

            //ThreadPool.SetMaxThreads(1, 1);
            //ThreadPool.SetMinThreads(1, 1);

            jobs.GetList().ForEach(item =>
            {
                AutoResetEvent autoevent = new AutoResetEvent(false);
                waitEvents.Add(autoevent);

                ThreadContext context = new ThreadContext();
                context.Autoevent = autoevent;

                context.client = new SmtpClient();
                context.client.EnableSsl = true;
                context.client.Timeout = TimeSpan.FromSeconds(300).Milliseconds;
                context.item = item;

                //ThreadPool.QueueUserWorkItem(delegate(object obj) {
                //    context = (ThreadContext)obj;

                    Send(context);

                //    context.Autoevent.Set();
                //}, context);
            });
            if (waitEvents.Count > 0) {
                bool wait = WaitHandle.WaitAll(waitEvents.ToArray(), TimeSpan.FromMinutes(30), true);
                if (!wait) {
                    Debug.WriteLine("Timeout occurred.");
                }
            }
        }
        private static void Send(ThreadContext context) {
            SmtpClient client = context.client;
            VwMailJob item = context.item;
            try {
                using (MailMessage message = new MailMessage()) {

                    message.From = new MailAddress(item.EmailFrom);
                    message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
                    message.Sender = message.From;
                    message.To.Add(new MailAddress(item.EmailTo));

                    message.Subject = item.Subject;
                    message.SubjectEncoding = Encoding.UTF8;

                    message.Headers.Add("X-SME-EmailID", item.Id.ToString());
                    message.Headers.Add("X-SME-Attempts", ((item.AttemptNumber ?? 0) + 1).ToString());
                    message.Headers.Add("X-SME-AppDomain", AppDomain.CurrentDomain.FriendlyName);
                    message.Headers.Add("X-SME-Sender", item.EmailFrom);

                    message.IsBodyHtml = true;
                    message.Body = item.Message;
                    message.BodyEncoding = Encoding.UTF8;

                    client.Send(message);

                    EmailDeQueue(item.Id);
                }

            } catch (Exception e) {
                SetFailedAtempt(item.Id, e.Message);
                Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
            }
        }
        private static void EmailDeQueue(int id) {
            MailJob job = MailJob.FetchByID(id);
            try {
                job.Sent = DateTime.Now;
                job.Save();
            } catch (Exception e) {
                Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
            }
        }

        private static void SetFailedAtempt(int id, string error) {
            MailJob job = MailJob.FetchByID(id);
            try {
                int atempt = job.AttemptNumber ?? 0;
                job.AttemptNumber = ++atempt;
                job.LastAttemptDate = DateTime.Now;
                job.LastAtemptError = error;
                job.Save();
            } catch (Exception e) {
                Debug.WriteLine(e.Message + " : " + (e.InnerException != null ? e.InnerException.Message : String.Empty));
            }
        }
    }
}